package de.koller.aframe.util.math;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

public class FunctionPreview {
	
	private static boolean SCALE = true;
	private static float MIN_Y = 0;
	public static int PANEL_WIDTH = 1200, PANEL_HEIGHT = 600;
	public static int[] COLORS = new int[] { 0xffff0000, 0xff0000ff, 0xff00ff00	};
	
	private FunctionPreview() {}
	
	public static void dontscale( float minY ) {
		SCALE = false;
		MIN_Y = minY;
	}
	
	public static void showFunction( float beginX, float endX, IFunction... functions ) {
		JOptionPane.showMessageDialog( 
				null, 
				new FunctionPanel( functions, beginX, endX ), 
				"Funktionsvorschau", 
				JOptionPane.PLAIN_MESSAGE );
		
		//SCALE = true;
	}
	
	private static class FunctionPanel extends JPanel implements MouseMotionListener {

		private float maxValue = Float.MIN_VALUE;
		private float minValue = Float.MAX_VALUE;
		private final float valueFrom;
		private final float valueUntil;
		private final float[][] results;
		
		private int mx, my;
		
		private FunctionPanel( IFunction[] functions, float valueFrom, float valueUntil ) {
			super();
			this.setPreferredSize( new Dimension( PANEL_WIDTH, PANEL_HEIGHT ) );
			this.setBackground( Color.WHITE );
			
			this.valueFrom = valueFrom;
			this.valueUntil = valueUntil;
			
			results = new float[ functions.length ][ PANEL_WIDTH+1 ];
			for( int i = 0; i < functions.length; i++ )
				fill( results[i], functions[i] );
			
			this.addMouseMotionListener( this );
		}

		@Override public void mouseDragged( MouseEvent e ) {}

		@Override
		public void mouseMoved( MouseEvent e ) {
			this.mx = e.getX();
			this.my = e.getY();
			this.repaint();
		}

		private void fill( float[] array, IFunction function ) {
			final float stepLength = (valueUntil - valueFrom) / PANEL_WIDTH;
			int i = 0;
			for( float x = valueFrom; x < valueUntil; x += stepLength ) {
				float f = function.getValue( x );
				array[ i ] = f;
				i++;
				
				maxValue = Math.max( maxValue, f );
				minValue = Math.min( minValue, f );
			}
			
			if( !SCALE ) {
				float pw = (valueUntil - valueFrom) / (float)PANEL_WIDTH;
				minValue = MIN_Y;
				maxValue = PANEL_HEIGHT * pw + minValue;
			}
		}
		
		@Override
		protected void paintComponent( Graphics g ) {
			super.paintComponent( g );
			
			Graphics2D g2 = (Graphics2D)g;
			
			renderGrid( g2 );
			
			int i = 0;
			for( float[] f : results ) {
				g.setColor( new Color( COLORS[i] ) );
				renderFunction( g2, f );
				i = (i+1) % COLORS.length;
			}
			
			g.setFont( new Font( Font.DIALOG, Font.PLAIN, 10 ) );
			String s = "f(" + gridX( mx ) + ") = ";
			for( i = 0; i < results.length; i++ ) {
				g.setColor( new Color( COLORS[ i % COLORS.length ] ) );
				float f = results[i][mx];
				g.drawString( s + f, 2, (i+1) * 12 );
			}
		}
		
		private void renderGrid( Graphics2D g ) {
			g.setColor( Color.LIGHT_GRAY );
			
			g.setStroke( new BasicStroke( 2 ) );
			int nilX = viewX( 0 );
			int nilY = viewY( 0 );
			g.drawLine( 0, nilY, PANEL_WIDTH, nilY );
			g.drawLine( nilX, 0, nilX, PANEL_HEIGHT );
			g.setStroke( new BasicStroke( 1 ) );
		}
		
		private void renderFunction( Graphics2D g, float[] f ) {
			for( int i = 0; i < f.length-1; i++ )
				g.drawLine( i, viewY( f[i] ), i+1, viewY( f[i+1] ) );
		}
		
		private int viewX( float x ) {
			float xstep = (valueUntil - valueFrom) / PANEL_WIDTH;
			if( xstep == 0 ) xstep = 1;
			int v = (int)((x - valueFrom) / xstep);
			return v;
		}
		
		private float gridX( int vx ) {
			final float stepLength = (valueUntil - valueFrom) / PANEL_WIDTH;
			return valueFrom + vx*stepLength;
		}
		
		private int viewY( float f ) {
			float ystep = (maxValue - minValue) / PANEL_HEIGHT;
			if( ystep == 0 ) ystep = 1;
			int v = PANEL_HEIGHT - (int)((f - minValue) / ystep);
			return v;
		}
		
	}
	
	
	public static void main( String[] args ) {
		FunctionPreview.dontscale( -1 );
		FunctionPreview.showFunction( -1, 1, new Function1() );
	}
	
	private static class Function1 implements IFunction {
		@Override public float getValue( float x ) {
			return x;
		}
	}
	
	private static class Function2 implements IFunction {
		@Override public float getValue( float x ) {
			return x*x;
		}
	}
	
	private static class Function3 implements IFunction {
		@Override public float getValue( float x ) {
			return (float)Math.exp( x );
		}
	}
}
